package ms.maplestory.boot.loaders;

import lombok.extern.slf4j.Slf4j;
import ms.maplestory.boot.ServerConstants;
import ms.maplestory.boot.world.field.objects.npc.Npc;
import ms.maplestory.boot.util.Util;
import ms.maplestory.boot.util.XMLApi;
import org.w3c.dom.Node;

import java.io.*;
import java.util.HashSet;
import java.util.Set;

@Slf4j
public class NpcData {

    private static Set<Npc> npcs = new HashSet<>();

    public static void generateDatFiles() {
        log.info("Started generating npc data.");
        long start = System.currentTimeMillis();
        loadNpcsFromWz();
        saveNpcsToDat(ServerConstants.DAT_DIR + "/npc");
        log.info(String.format("Completed generating npc data in %dms.", System.currentTimeMillis() - start));
    }

    private static void loadNpcsFromWz() {
        String wzDir = String.format("%s/Npc.wz", ServerConstants.WZ_DIR);
        for (File file : new File(wzDir).listFiles()) {
            Npc npc = new Npc(0);
            Node node = XMLApi.getRoot(file);
            Node mainNode = XMLApi.getAllChildren(node).get(0);
            int id = Integer.parseInt(XMLApi.getNamedAttribute(mainNode, "name")
                    .replace(".xml", "").replace(".img", ""));
            npc.setId(id);
            npc.setMove(XMLApi.getFirstChildByNameBF(mainNode, "move") != null);
            Node scriptNode = XMLApi.getFirstChildByNameBF(mainNode, "script");
            if (scriptNode != null) {
                for (Node idNode : XMLApi.getAllChildren(scriptNode)) {
                    String scriptIDString = XMLApi.getNamedAttribute(idNode, "name");
                    if (!Util.isNumber(scriptIDString)) {
                        continue;
                    }
                    int scriptID = Integer.parseInt(XMLApi.getNamedAttribute(idNode, "name"));
                    Node scriptValueNode = XMLApi.getFirstChildByNameDF(idNode, "script");
                    if (scriptValueNode != null) {
                        String scriptName = XMLApi.getNamedAttribute(scriptValueNode, "value");
                        npc.getScripts().put(scriptID, scriptName);
                    }
                }
            }
            Node infoNode = XMLApi.getFirstChildByNameBF(mainNode, "info");
            for (Node infoChildNode : XMLApi.getAllChildren(infoNode)) {
                String name = XMLApi.getNamedAttribute(infoChildNode, "name");
                String value = XMLApi.getNamedAttribute(infoChildNode, "value");
                switch (name) {
                    case "trunkGet":
                        npc.setTrunkGet(Integer.parseInt(value));
                        break;
                    case "trunkPut":
                        npc.setTrunkPut(Integer.parseInt(value));
                        break;
                }
            }
            npcs.add(npc);
        }
    }

    public static void saveNpcsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        for (Npc npc : npcs) {
            File file = new File(String.format("%s/%d.dat", dir, npc.getId()));
            try (DataOutputStream das = new DataOutputStream(new FileOutputStream(file))) {
                das.writeInt(npc.getId());
                das.writeBoolean(npc.isMove());
                das.writeInt(npc.getTrunkGet());
                das.writeInt(npc.getTrunkPut());
                das.writeShort(npc.getScripts().size());
                npc.getScripts().forEach((key, val) -> {
                    try {
                        das.writeInt(key);
                        das.writeUTF(val);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static Npc getNpcDeepCopyById(int id) {
        Npc res = getNpc(id);
        if (res != null) {
            res = res.deepCopy();
        } else {
            File file = new File(String.format("%s/npc/%d.dat", ServerConstants.DAT_DIR, id));
            if (file.exists()) {
                res = loadNpcFromDat(file).deepCopy();
                npcs.add(res);
            }
        }
        return res;
    }

    private static Npc getNpc(int id) {
        return npcs.stream().filter(npc -> npc.getId() == id).findFirst().orElse(null);
    }

    private static Npc loadNpcFromDat(File file) {
        try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {
            Npc npc = new Npc(dis.readInt());
            npc.setMove(dis.readBoolean());
            npc.setTrunkGet(dis.readInt());
            npc.setTrunkPut(dis.readInt());
            short size = dis.readShort();
            for (int i = 0; i < size; i++) {
                int id = dis.readInt();
                String val = dis.readUTF();
                npc.getScripts().put(id, val);
            }
            npcs.add(npc);
            return npc;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}
